home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_ents.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  19.8 KB  |  772 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_ents.c -- present snapshot entities, happens every single frame
  4.  
  5. #include "cg_local.h"
  6.  
  7.  
  8. /*
  9. ======================
  10. CG_PositionEntityOnTag
  11.  
  12. Modifies the entities position and axis by the given
  13. tag location
  14. ======================
  15. */
  16. void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
  17.                             qhandle_t parentModel, char *tagName ) {
  18.     int                i;
  19.     orientation_t    lerped;
  20.     
  21.     // lerp the tag
  22.     trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
  23.         1.0 - parent->backlerp, tagName );
  24.  
  25.     // FIXME: allow origin offsets along tag?
  26.     VectorCopy( parent->origin, entity->origin );
  27.     for ( i = 0 ; i < 3 ; i++ ) {
  28.         VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
  29.     }
  30.  
  31.     // had to cast away the const to avoid compiler problems...
  32.     MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
  33.     entity->backlerp = parent->backlerp;
  34. }
  35.  
  36.  
  37. /*
  38. ======================
  39. CG_PositionRotatedEntityOnTag
  40.  
  41. Modifies the entities position and axis by the given
  42. tag location
  43. ======================
  44. */
  45. void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
  46.                             qhandle_t parentModel, char *tagName ) {
  47.     int                i;
  48.     orientation_t    lerped;
  49.     vec3_t            tempAxis[3];
  50.  
  51. //AxisClear( entity->axis );
  52.     // lerp the tag
  53.     trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
  54.         1.0 - parent->backlerp, tagName );
  55.  
  56.     // FIXME: allow origin offsets along tag?
  57.     VectorCopy( parent->origin, entity->origin );
  58.     for ( i = 0 ; i < 3 ; i++ ) {
  59.         VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
  60.     }
  61.  
  62.     // had to cast away the const to avoid compiler problems...
  63.     MatrixMultiply( entity->axis, lerped.axis, tempAxis );
  64.     MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
  65. }
  66.  
  67.  
  68.  
  69. /*
  70. ==========================================================================
  71.  
  72. FUNCTIONS CALLED EACH FRAME
  73.  
  74. ==========================================================================
  75. */
  76.  
  77. /*
  78. ======================
  79. CG_SetEntitySoundPosition
  80.  
  81. Also called by event processing code
  82. ======================
  83. */
  84. void CG_SetEntitySoundPosition( centity_t *cent ) {
  85.     if ( cent->currentState.solid == SOLID_BMODEL ) {
  86.         vec3_t    origin;
  87.         float    *v;
  88.  
  89.         v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
  90.         VectorAdd( cent->lerpOrigin, v, origin );
  91.         trap_S_UpdateEntityPosition( cent->currentState.number, origin );
  92.     } else {
  93.         trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
  94.     }
  95. }
  96.  
  97. /*
  98. ==================
  99. CG_EntityEffects
  100.  
  101. Add continuous entity effects, like local entity emission and lighting
  102. ==================
  103. */
  104. static void CG_EntityEffects( centity_t *cent ) {
  105.  
  106.     // update sound origins
  107.     CG_SetEntitySoundPosition( cent );
  108.  
  109.     // add loop sound
  110.     if ( cent->currentState.loopSound ) {
  111.         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
  112.             cgs.gameSounds[ cent->currentState.loopSound ] );
  113.     }
  114.  
  115.  
  116.     // constant light glow
  117.     if ( cent->currentState.constantLight ) {
  118.         int        cl;
  119.         int        i, r, g, b;
  120.  
  121.         cl = cent->currentState.constantLight;
  122.         r = cl & 255;
  123.         g = ( cl >> 8 ) & 255;
  124.         b = ( cl >> 16 ) & 255;
  125.         i = ( ( cl >> 24 ) & 255 ) * 4;
  126.         trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
  127.     }
  128.  
  129. }
  130.  
  131.  
  132. /*
  133. ==================
  134. CG_General
  135. ==================
  136. */
  137. static void CG_General( centity_t *cent ) {
  138.     refEntity_t            ent;
  139.     entityState_t        *s1;
  140.  
  141.     s1 = ¢->currentState;
  142.  
  143.     // if set to invisible, skip
  144.     if (!s1->modelindex) {
  145.         return;
  146.     }
  147.  
  148.     memset (&ent, 0, sizeof(ent));
  149.  
  150.     // set frame
  151.  
  152.     ent.frame = s1->frame;
  153.     ent.oldframe = ent.frame;
  154.     ent.backlerp = 0;
  155.  
  156.     VectorCopy( cent->lerpOrigin, ent.origin);
  157.     VectorCopy( cent->lerpOrigin, ent.oldorigin);
  158.  
  159.     ent.hModel = cgs.gameModels[s1->modelindex];
  160.  
  161.     // player model
  162.     if (s1->number == cg.snap->ps.clientNum) {
  163.         ent.renderfx |= RF_THIRD_PERSON;    // only draw from mirrors
  164.     }
  165.  
  166.     // convert angles to axis
  167.     AnglesToAxis( cent->lerpAngles, ent.axis );
  168.  
  169.     // add to refresh list
  170.     trap_R_AddRefEntityToScene (&ent);
  171. }
  172.  
  173. /*
  174. ==================
  175. CG_Speaker
  176.  
  177. Speaker entities can automatically play sounds
  178. ==================
  179. */
  180. static void CG_Speaker( centity_t *cent ) {
  181.     if ( ! cent->currentState.clientNum ) {    // FIXME: use something other than clientNum...
  182.         return;        // not auto triggering
  183.     }
  184.  
  185.     if ( cg.time < cent->miscTime ) {
  186.         return;
  187.     }
  188.  
  189.     trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
  190.  
  191.     //    ent->s.frame = ent->wait * 10;
  192.     //    ent->s.clientNum = ent->random * 10;
  193.     cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
  194. }
  195.  
  196. /*
  197. ==================
  198. CG_Item
  199. ==================
  200. */
  201. static void CG_Item( centity_t *cent ) {
  202.     refEntity_t            ent;
  203.     entityState_t        *es;
  204.     gitem_t                *item;
  205.     int                    msec;
  206.     float                frac;
  207.     float                scale;
  208.  
  209.     es = ¢->currentState;
  210.     if ( es->modelindex >= bg_numItems ) {
  211.         CG_Error( "Bad item index %i on entity", es->modelindex );
  212.     }
  213.  
  214.     // if set to invisible, skip
  215.     if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
  216.         return;
  217.     }
  218.  
  219.     item = &bg_itemlist[ es->modelindex ];
  220.     if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
  221.         memset( &ent, 0, sizeof( ent ) );
  222.         ent.reType = RT_SPRITE;
  223.         VectorCopy( cent->lerpOrigin, ent.origin );
  224.         ent.radius = 14;
  225.         ent.customShader = cg_items[es->modelindex].icon;
  226.         ent.shaderRGBA[0] = 255;
  227.         ent.shaderRGBA[1] = 255;
  228.         ent.shaderRGBA[2] = 255;
  229.         ent.shaderRGBA[3] = 255;
  230.         trap_R_AddRefEntityToScene(&ent);
  231.         return;
  232.     }
  233.  
  234.     // items bob up and down continuously
  235.     scale = 0.005 + cent->currentState.number * 0.00001;
  236.     cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) *  scale ) * 4;
  237.  
  238.     memset (&ent, 0, sizeof(ent));
  239.  
  240.     // autorotate at one of two speeds
  241.     if ( item->giType == IT_HEALTH ) {
  242.         VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
  243.         AxisCopy( cg.autoAxisFast, ent.axis );
  244.     } else {
  245.         VectorCopy( cg.autoAngles, cent->lerpAngles );
  246.         AxisCopy( cg.autoAxis, ent.axis );
  247.     }
  248.  
  249.     // the weapons have their origin where they attatch to player
  250.     // models, so we need to offset them or they will rotate
  251.     // eccentricly
  252.     if ( item->giType == IT_WEAPON ) {
  253.         weaponInfo_t    *wi;
  254.  
  255.         wi = &cg_weapons[item->giTag];
  256.         cent->lerpOrigin[0] -= 
  257.             wi->weaponMidpoint[0] * ent.axis[0][0] +
  258.             wi->weaponMidpoint[1] * ent.axis[1][0] +
  259.             wi->weaponMidpoint[2] * ent.axis[2][0];
  260.         cent->lerpOrigin[1] -= 
  261.             wi->weaponMidpoint[0] * ent.axis[0][1] +
  262.             wi->weaponMidpoint[1] * ent.axis[1][1] +
  263.             wi->weaponMidpoint[2] * ent.axis[2][1];
  264.         cent->lerpOrigin[2] -= 
  265.             wi->weaponMidpoint[0] * ent.axis[0][2] +
  266.             wi->weaponMidpoint[1] * ent.axis[1][2] +
  267.             wi->weaponMidpoint[2] * ent.axis[2][2];
  268.  
  269.         cent->lerpOrigin[2] += 8;    // an extra height boost
  270.     }
  271.  
  272.     ent.hModel = cg_items[es->modelindex].models[0];
  273.  
  274.     VectorCopy( cent->lerpOrigin, ent.origin);
  275.     VectorCopy( cent->lerpOrigin, ent.oldorigin);
  276.  
  277.     ent.nonNormalizedAxes = qfalse;
  278.  
  279.     // if just respawned, slowly scale up
  280.     msec = cg.time - cent->miscTime;
  281.     if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
  282.         frac = (float)msec / ITEM_SCALEUP_TIME;
  283.         VectorScale( ent.axis[0], frac, ent.axis[0] );
  284.         VectorScale( ent.axis[1], frac, ent.axis[1] );
  285.         VectorScale( ent.axis[2], frac, ent.axis[2] );
  286.         ent.nonNormalizedAxes = qtrue;
  287.     } else {
  288.         frac = 1.0;
  289.     }
  290.  
  291.     // items without glow textures need to keep a minimum light value
  292.     // so they are always visible
  293.     if ( ( item->giType == IT_WEAPON ) ||
  294.          ( item->giType == IT_ARMOR ) ) {
  295.         ent.renderfx |= RF_MINLIGHT;
  296.     }
  297.  
  298.     // increase the size of the weapons when they are presented as items
  299.     if ( item->giType == IT_WEAPON ) {
  300.         VectorScale( ent.axis[0], 1.5, ent.axis[0] );
  301.         VectorScale( ent.axis[1], 1.5, ent.axis[1] );
  302.         VectorScale( ent.axis[2], 1.5, ent.axis[2] );
  303.         ent.nonNormalizedAxes = qtrue;
  304.     }
  305.  
  306.     // add to refresh list
  307.     trap_R_AddRefEntityToScene(&ent);
  308.  
  309.     // accompanying rings / spheres for powerups
  310.     if ( !cg_simpleItems.integer ) 
  311.     {
  312.         vec3_t spinAngles;
  313.  
  314.         VectorClear( spinAngles );
  315.  
  316.         if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
  317.         {
  318.             if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
  319.             {
  320.                 if ( item->giType == IT_POWERUP )
  321.                 {
  322.                     ent.origin[2] += 12;
  323.                     spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
  324.                 }
  325.                 AnglesToAxis( spinAngles, ent.axis );
  326.                 
  327.                 // scale up if respawning
  328.                 if ( frac != 1.0 ) {
  329.                     VectorScale( ent.axis[0], frac, ent.axis[0] );
  330.                     VectorScale( ent.axis[1], frac, ent.axis[1] );
  331.                     VectorScale( ent.axis[2], frac, ent.axis[2] );
  332.                     ent.nonNormalizedAxes = qtrue;
  333.                 }
  334.                 trap_R_AddRefEntityToScene( &ent );
  335.             }
  336.         }
  337.     }
  338. }
  339.  
  340. //============================================================================
  341.  
  342. /*
  343. ===============
  344. CG_Missile
  345. ===============
  346. */
  347. static void CG_Missile( centity_t *cent ) {
  348.     refEntity_t            ent;
  349.     entityState_t        *s1;
  350.     const weaponInfo_t        *weapon;
  351.  
  352.     s1 = ¢->currentState;
  353.     if ( s1->weapon > WP_NUM_WEAPONS ) {
  354.         s1->weapon = 0;
  355.     }
  356.     weapon = &cg_weapons[s1->weapon];
  357.  
  358.     // calculate the axis
  359.     VectorCopy( s1->angles, cent->lerpAngles);
  360.  
  361.     // add trails
  362.     if ( weapon->missileTrailFunc ) 
  363.     {
  364.         weapon->missileTrailFunc( cent, weapon );
  365.     }
  366.  
  367.     // add dynamic light
  368.     if ( weapon->missileDlight ) {
  369.         trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 
  370.             weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
  371.     }
  372.  
  373.     // add missile sound
  374.     if ( weapon->missileSound ) {
  375.         vec3_t    velocity;
  376.  
  377.         BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
  378.  
  379.         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
  380.     }
  381.  
  382.     // create the render entity
  383.     memset (&ent, 0, sizeof(ent));
  384.     VectorCopy( cent->lerpOrigin, ent.origin);
  385.     VectorCopy( cent->lerpOrigin, ent.oldorigin);
  386.  
  387.     if ( cent->currentState.weapon == WP_PLASMAGUN ) {
  388.         ent.reType = RT_SPRITE;
  389.         ent.radius = 16;
  390.         ent.rotation = 0;
  391.         ent.customShader = cgs.media.plasmaBallShader;
  392.         trap_R_AddRefEntityToScene( &ent );
  393.         return;
  394.     }
  395.  
  396.     // flicker between two skins
  397.     ent.skinNum = cg.clientFrame & 1;
  398.     ent.hModel = weapon->missileModel;
  399.     ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
  400.  
  401.     // convert direction of travel into axis
  402.     if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
  403.         ent.axis[0][2] = 1;
  404.     }
  405.  
  406.     // spin as it moves
  407.     if ( s1->pos.trType != TR_STATIONARY ) {
  408.         RotateAroundDirection( ent.axis, cg.time / 4 );
  409.     } else {
  410.         RotateAroundDirection( ent.axis, s1->time );
  411.     }
  412.  
  413.     // add to refresh list, possibly with quad glow
  414.     CG_AddRefEntityWithPowerups( &ent, s1->powerups, TEAM_FREE );
  415. }
  416.  
  417. /*
  418. ===============
  419. CG_Grapple
  420.  
  421. This is called when the grapple is sitting up against the wall
  422. ===============
  423. */
  424. static void CG_Grapple( centity_t *cent ) {
  425.     refEntity_t            ent;
  426.     entityState_t        *s1;
  427.     const weaponInfo_t        *weapon;
  428.  
  429.     s1 = ¢->currentState;
  430.     if ( s1->weapon > WP_NUM_WEAPONS ) {
  431.         s1->weapon = 0;
  432.     }
  433.     weapon = &cg_weapons[s1->weapon];
  434.  
  435.     // calculate the axis
  436.     VectorCopy( s1->angles, cent->lerpAngles);
  437.  
  438. #if 0 // FIXME add grapple pull sound here..?
  439.     // add missile sound
  440.     if ( weapon->missileSound ) {
  441.         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
  442.     }
  443. #endif
  444.  
  445.     // Will draw cable if needed
  446.     CG_GrappleTrail ( cent, weapon );
  447.  
  448.     // create the render entity
  449.     memset (&ent, 0, sizeof(ent));
  450.     VectorCopy( cent->lerpOrigin, ent.origin);
  451.     VectorCopy( cent->lerpOrigin, ent.oldorigin);
  452.  
  453.     // flicker between two skins
  454.     ent.skinNum = cg.clientFrame & 1;
  455.     ent.hModel = weapon->missileModel;
  456.     ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
  457.  
  458.     // convert direction of travel into axis
  459.     if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
  460.         ent.axis[0][2] = 1;
  461.     }
  462.  
  463.     trap_R_AddRefEntityToScene( &ent );
  464. }
  465.  
  466. /*
  467. ===============
  468. CG_Mover
  469. ===============
  470. */
  471. static void CG_Mover( centity_t *cent ) {
  472.     refEntity_t            ent;
  473.     entityState_t        *s1;
  474.  
  475.     s1 = ¢->currentState;
  476.  
  477.     // create the render entity
  478.     memset (&ent, 0, sizeof(ent));
  479.     VectorCopy( cent->lerpOrigin, ent.origin);
  480.     VectorCopy( cent->lerpOrigin, ent.oldorigin);
  481.     AnglesToAxis( cent->lerpAngles, ent.axis );
  482.  
  483.     ent.renderfx = RF_NOSHADOW;
  484.  
  485.     // flicker between two skins (FIXME?)
  486.     ent.skinNum = ( cg.time >> 6 ) & 1;
  487.  
  488.     // get the model, either as a bmodel or a modelindex
  489.     if ( s1->solid == SOLID_BMODEL ) {
  490.         ent.hModel = cgs.inlineDrawModel[s1->modelindex];
  491.     } else {
  492.         ent.hModel = cgs.gameModels[s1->modelindex];
  493.     }
  494.  
  495.     // add to refresh list
  496.     trap_R_AddRefEntityToScene(&ent);
  497.  
  498.     // add the secondary model
  499.     if ( s1->modelindex2 ) {
  500.         ent.skinNum = 0;
  501.         ent.hModel = cgs.gameModels[s1->modelindex2];
  502.         trap_R_AddRefEntityToScene(&ent);
  503.     }
  504.  
  505. }
  506.  
  507. /*
  508. ===============
  509. CG_Beam
  510.  
  511. Also called as an event
  512. ===============
  513. */
  514. void CG_Beam( centity_t *cent ) {
  515.     refEntity_t            ent;
  516.     entityState_t        *s1;
  517.  
  518.     s1 = ¢->currentState;
  519.  
  520.     // create the render entity
  521.     memset (&ent, 0, sizeof(ent));
  522.     VectorCopy( s1->pos.trBase, ent.origin );
  523.     VectorCopy( s1->origin2, ent.oldorigin );
  524.     AxisClear( ent.axis );
  525.     ent.reType = RT_BEAM;
  526.  
  527.     ent.renderfx = RF_NOSHADOW;
  528.  
  529.     // add to refresh list
  530.     trap_R_AddRefEntityToScene(&ent);
  531. }
  532.  
  533.  
  534. /*
  535. ===============
  536. CG_Portal
  537. ===============
  538. */
  539. static void CG_Portal( centity_t *cent ) {
  540.     refEntity_t            ent;
  541.     entityState_t        *s1;
  542.  
  543.     s1 = ¢->currentState;
  544.  
  545.     // create the render entity
  546.     memset (&ent, 0, sizeof(ent));
  547.     VectorCopy( cent->lerpOrigin, ent.origin );
  548.     VectorCopy( s1->origin2, ent.oldorigin );
  549.     ByteToDir( s1->eventParm, ent.axis[0] );
  550.     PerpendicularVector( ent.axis[1], ent.axis[0] );
  551.  
  552.     // negating this tends to get the directions like they want
  553.     // we really should have a camera roll value
  554.     VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
  555.  
  556.     CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
  557.     ent.reType = RT_PORTALSURFACE;
  558.     ent.frame = s1->frame;        // rotation speed
  559.     ent.skinNum = s1->clientNum/256.0 * 360;    // roll offset
  560.  
  561.     // add to refresh list
  562.     trap_R_AddRefEntityToScene(&ent);
  563. }
  564.  
  565.  
  566. /*
  567. =========================
  568. CG_AdjustPositionForMover
  569.  
  570. Also called by client movement prediction code
  571. =========================
  572. */
  573. void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
  574.     centity_t    *cent;
  575.     vec3_t    oldOrigin, origin, deltaOrigin;
  576.     vec3_t    oldAngles, angles, deltaAngles;
  577.  
  578.     if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
  579.         VectorCopy( in, out );
  580.         return;
  581.     }
  582.  
  583.     cent = &cg_entities[ moverNum ];
  584.     if ( cent->currentState.eType != ET_MOVER ) {
  585.         VectorCopy( in, out );
  586.         return;
  587.     }
  588.  
  589.     BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin );
  590.     BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles );
  591.  
  592.     BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin );
  593.     BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles );
  594.  
  595.     VectorSubtract( origin, oldOrigin, deltaOrigin );
  596.     VectorSubtract( angles, oldAngles, deltaAngles );
  597.  
  598.     VectorAdd( in, deltaOrigin, out );
  599.  
  600.     // FIXME: origin change when on a rotating object
  601. }
  602.  
  603.  
  604. /*
  605. =============================
  606. CG_InterpolateEntityPosition
  607. =============================
  608. */
  609. static void CG_InterpolateEntityPosition( centity_t *cent ) {
  610.     vec3_t        current, next;
  611.     float        f;
  612.  
  613.     // it would be an internal error to find an entity that interpolates without
  614.     // a snapshot ahead of the current one
  615.     if ( cg.nextSnap == NULL ) {
  616.         CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
  617.     }
  618.  
  619.     f = cg.frameInterpolation;
  620.  
  621.     // this will linearize a sine or parabolic curve, but it is important
  622.     // to not extrapolate player positions if more recent data is available
  623.     BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current );
  624.     BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next );
  625.  
  626.     cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
  627.     cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
  628.     cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
  629.  
  630.     BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current );
  631.     BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next );
  632.  
  633.     cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
  634.     cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
  635.     cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
  636.  
  637. }
  638.  
  639. /*
  640. ===============
  641. CG_CalcEntityLerpPositions
  642.  
  643. ===============
  644. */
  645. static void CG_CalcEntityLerpPositions( centity_t *cent ) {
  646.     if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
  647.         CG_InterpolateEntityPosition( cent );
  648.         return;
  649.     }
  650.     
  651.     // just use the current frame and evaluate as best we can
  652.     BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
  653.     BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
  654.  
  655.     // adjust for riding a mover if it wasn't rolled into the predicted
  656.     // player state
  657.     if ( cent != &cg.predictedPlayerEntity ) {
  658.         CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, 
  659.         cg.snap->serverTime, cg.time, cent->lerpOrigin );
  660.     }
  661. }
  662.  
  663. /*
  664. ===============
  665. CG_AddCEntity
  666.  
  667. ===============
  668. */
  669. static void CG_AddCEntity( centity_t *cent ) {
  670.     // event-only entities will have been dealt with already
  671.     if ( cent->currentState.eType >= ET_EVENTS ) {
  672.         return;
  673.     }
  674.  
  675.     // calculate the current origin
  676.     CG_CalcEntityLerpPositions( cent );
  677.  
  678.     // add automatic effects
  679.     CG_EntityEffects( cent );
  680.  
  681.     switch ( cent->currentState.eType ) {
  682.     default:
  683.         CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
  684.         break;
  685.     case ET_INVISIBLE:
  686.     case ET_PUSH_TRIGGER:
  687.     case ET_TELEPORT_TRIGGER:
  688.         break;
  689.     case ET_GENERAL:
  690.         CG_General( cent );
  691.         break;
  692.     case ET_PLAYER:
  693.         CG_Player( cent );
  694.         break;
  695.     case ET_ITEM:
  696.         CG_Item( cent );
  697.         break;
  698.     case ET_MISSILE:
  699.         CG_Missile( cent );
  700.         break;
  701.     case ET_MOVER:
  702.         CG_Mover( cent );
  703.         break;
  704.     case ET_BEAM:
  705.         CG_Beam( cent );
  706.         break;
  707.     case ET_PORTAL:
  708.         CG_Portal( cent );
  709.         break;
  710.     case ET_SPEAKER:
  711.         CG_Speaker( cent );
  712.         break;
  713.     case ET_GRAPPLE:
  714.         CG_Grapple( cent );
  715.         break;
  716.     }
  717. }
  718.  
  719. /*
  720. ===============
  721. CG_AddPacketEntities
  722.  
  723. ===============
  724. */
  725. void CG_AddPacketEntities( void ) {
  726.     int                    num;
  727.     centity_t            *cent;
  728.     playerState_t        *ps;
  729.  
  730.     // set cg.frameInterpolation
  731.     if ( cg.nextSnap ) {
  732.         int        delta;
  733.  
  734.         delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
  735.         if ( delta == 0 ) {
  736.             cg.frameInterpolation = 0;
  737.         } else {
  738.             cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
  739.         }
  740.     } else {
  741.         cg.frameInterpolation = 0;    // actually, it should never be used, because 
  742.                                     // no entities should be marked as interpolating
  743.     }
  744.  
  745.     // the auto-rotating items will all have the same axis
  746.     cg.autoAngles[0] = 0;
  747.     cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
  748.     cg.autoAngles[2] = 0;
  749.  
  750.     cg.autoAnglesFast[0] = 0;
  751.     cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
  752.     cg.autoAnglesFast[2] = 0;
  753.  
  754.     AnglesToAxis( cg.autoAngles, cg.autoAxis );
  755.     AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
  756.  
  757.     // generate and add the entity from the playerstate
  758.     ps = &cg.predictedPlayerState;
  759.     BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
  760.     CG_AddCEntity( &cg.predictedPlayerEntity );
  761.  
  762.     // lerp the non-predicted value for lightning gun origins
  763.     CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
  764.  
  765.     // add each entity sent over by the server
  766.     for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
  767.         cent = &cg_entities[ cg.snap->entities[ num ].number ];
  768.         CG_AddCEntity( cent );
  769.     }
  770. }
  771.  
  772.